﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using log4net;
using NHibernate;
using NHibernate.Criterion;
using TeamCommunitySolution.Domain.DataLayer.PlayerMetrics;
using TeamCommunitySolution.Repositories.Providers;

namespace TeamCommunitySolution.Repositories.Metrics
{
    public class MetricsRepositoryImpl : MetricsRepository
    {
        /// <summary>
        /// The log instance.
        /// </summary>
        private static readonly ILog _log = LogManager.GetLogger(typeof(MetricsRepository));

        /// <summary>
        /// Creates the instance.
        /// </summary>
        /// <returns>Metrics Repository Instance.</returns>
        public static MetricsRepository CreateInstance()
        {
            return new MetricsRepositoryImpl();
        }

        /// <summary>
        /// Saves the specified metric group.
        /// </summary>
        /// <param name="metricGroup">The metric group.</param>
        public void Save(ref MetricGroup metricGroup)
        {
            using (ISession session = Nhibernate.Factory.OpenSession())
            {
                var scope = session.BeginTransaction();
                try
                {
                    metricGroup = (MetricGroup)session.SaveOrUpdateCopy(metricGroup);
                    scope.Commit();
                }
                catch (Exception ex)
                {
                    scope.Rollback();
                    string message = "Unable to save object [Metric Group] in the datastore";
                    this.HandleException(message, ex);
                }
            }
        }

        /// <summary>
        /// Saves the specified metric definition.
        /// </summary>
        /// <param name="metricDefinition">The metric definition.</param>
        public void Save(ref MetricDefinition metricDefinition)
        {
            using (ISession session = Nhibernate.Factory.OpenSession())
            {
                var scope = session.BeginTransaction();
                try
                {
                    metricDefinition = (MetricDefinition)session.SaveOrUpdateCopy(metricDefinition);
                    scope.Commit();
                }
                catch (Exception ex)
                {
                    scope.Rollback();
                    string message = "Unable to save object [Metric Definition] in the datastore";
                    this.HandleException(message, ex);
                }
            }
        }

        /// <summary>
        /// Saves the specified metric content.
        /// </summary>
        /// <param name="metricContent">Content of the metric.</param>
        public void Save(ref MetricContent metricContent)
        {
            using (ISession session = Nhibernate.Factory.OpenSession())
            {
                var scope = session.BeginTransaction();
                try
                {
                    if(metricContent.DateCaptured.Equals(DateTime.MinValue))
                    {
                        metricContent.DateCaptured = DateTime.Now;
                    }
                    metricContent = (MetricContent)session.SaveOrUpdateCopy(metricContent);
                    scope.Commit();
                }
                catch (Exception ex)
                {
                    scope.Rollback();
                    string message = "Unable to save object [Metric Content] in the datastore";
                    this.HandleException(message, ex);
                }
            }
        }

        /// <summary>
        /// Gets the metric group by field.
        /// </summary>
        /// <param name="fieldName">Name of the field.</param>
        /// <param name="fieldValue">The field value.</param>
        /// <returns>Metric Group Instance.</returns>
        public MetricGroup GetMetricGroupByField(string fieldName, string fieldValue)
        {
            using (ISession session = Nhibernate.Factory.OpenSession())
            {
                ICriteria crit = session.CreateCriteria(typeof(MetricGroup));
                crit.Add(Expression.Eq(fieldName, fieldValue));

                IList<MetricGroup> results = crit.List<MetricGroup>();

                if (results.Count > 1)
                {
                    throw new MetricsRepositoryException(string.Format("Duplicate objects [MetricGroup] found in the datastore. Field: {0}, Value: {1}", fieldName, fieldValue));
                }

                if (results.Count == 0)
                {
                    return null;
                }

                return results[0];
            }
        }

        /// <summary>
        /// Gets the metric definition by field.
        /// </summary>
        /// <param name="fieldName">Name of the field.</param>
        /// <param name="fieldValue">The field value.</param>
        /// <returns>Metric Definition Instance.</returns>
        public MetricDefinition GetMetricDefinitionByField(string fieldName, string fieldValue)
        {
            using (ISession session = Nhibernate.Factory.OpenSession())
            {
                ICriteria crit = session.CreateCriteria(typeof(MetricDefinition));
                crit.Add(Expression.Eq(fieldName, fieldValue));

                IList<MetricDefinition> results = crit.List<MetricDefinition>();

                if (results.Count > 1)
                {
                    throw new MetricsRepositoryException(string.Format("Duplicate objects [MetricDefinition] found in the datastore. Field: {0}, Value: {1}", fieldName, fieldValue));
                }

                if (results.Count == 0)
                {
                    return null;
                }

                return results[0];
            }
        }

        /// <summary>
        /// Handles the exception.
        /// </summary>
        /// <param name="message">The message.</param>
        /// <param name="ex">The exception.</param>
        private void HandleException(string message, Exception ex)
        {
            _log.Error(message, ex);
            throw new MetricsRepositoryException(message, ex);
        }
    }
}
